/*
Web server endpoints for the gce-xfstests LTM (lightweight test manager).

This stand-alone server handles requests sent by the client-side scripts.
The endpoints are:
	/login - authenticates a user session, implemented in server.go

	/gce-xfstests - takes in a json POST in the form of LTMRequest, and runs the
	tests.

	/internal - handles internal requests from KCS server.

	/status - handles queries for running status from user.

*/
package main

import (
	"net/http"

	"thunk.org/gce-server/util/check"
	"thunk.org/gce-server/util/logging"
	"thunk.org/gce-server/util/mymath"
	"thunk.org/gce-server/util/server"

	"github.com/sirupsen/logrus"
)

/*
runTests is the endpoint for a gce-xfstests test request from user.

orig_cmdline is a base64 encoding of the command line arguments.
A ShardScheduler is constructed to arrange the tests in multiple
ShardWorkers, and then starts these shards in separate go routines.
Returns the info generated by the sharder
*/
func runTests(w http.ResponseWriter, r *http.Request, log *logrus.Entry) {
	log = log.WithField("endpoint", "/gce-xfstests")

	c, err := server.ParseTaskRequest(w, r)
	check.Panic(err, log, "Failed to parse request")
	log.WithFields(logrus.Fields{
		"cmdLine":      c.CmdLine,
		"options":      c.Options,
		"extraOptions": c.ExtraOptions,
	}).Info("Received test request")

	testID := mymath.GetTimeStamp()

	if c.ExtraOptions == nil {
		log.WithField("testID", testID).Info("User request, generating testID")
	} else {
		testID = c.ExtraOptions.TestID
		log.WithField("testID", testID).Info("KCS request, use existing testID")
	}

	response := server.SimpleResponse{
		Status: true,
		TestID: testID,
	}

	if c.ExtraOptions == nil {
		if c.Options.UnWatch != "" {
			log.Info("User requests a git unwatch, terminating git repo monitor")
			StopWatcher(c)

			response.Msg = "Git repo monitor terminated"
			response.TestID = ""

		} else if c.Options.BranchName != "" {
			log.Info("User requests a git watch, launching git repo monitor")
			watcher := NewGitWatcher(c, testID)
			go watcher.Run()

			response.Msg = "Git repo monitor initiating"

		} else if c.Options.BadCommit != "" && c.Options.GoodCommit != "" {
			log.Info("User requests a git bisect, forwarding to KCS")
			c.ExtraOptions = &server.InternalOptions{
				TestID:    testID,
				Requester: server.LTMBisectStart,
			}
			go ForwardKCS(c, testID)

			response.Msg = "Calling KCS to initiate git bisect"

		} else if c.Options.CommitID != "" {
			log.Info("User requests a kernel build, forwarding to KCS")
			c.ExtraOptions = &server.InternalOptions{
				TestID:    testID,
				Requester: server.LTMBuild,
			}
			go ForwardKCS(c, testID)

			response.Msg = "Calling KCS to build kernel"
		}
	}

	if response.Msg == "" {
		var sharder *ShardScheduler

		if logging.MOCK {
			sharder = MockNewShardScheduler(c, testID)
			// sharder.Dump("/root/mock_sharder.json")
			// sharder := ReadSharder("/root/mock_sharder.json")
			log.Info("Mock sharder created")
			go sharder.MockRun()

		} else {
			sharder = NewShardScheduler(c, testID)
			log.Info("Test sharder created")
			go sharder.Run()
		}

		response.Msg = "Launching tests"
	}

	log.WithField("response", response).Info("Sending response")
	err = server.SendResponse(w, r, response)
	check.Panic(err, log, "Failed to send the response")
}

// status is the endpoint for querying running status.
// It calls KCS to collect building and bisector status.
func status(w http.ResponseWriter, r *http.Request, serverLog *logrus.Entry) {
	log := serverLog.WithField("endpoint", "/status")
	log.Info("generating running status info")

	KCSStatus := server.InternalQuery(log)

	response := server.StatusResponse{
		Sharders:  SharderStatus(),
		Watchers:  WatcherStatus(),
		Bisectors: KCSStatus.Bisectors,
	}
	log.WithField("response", response).Info("Sending response")

	err := server.SendResponse(w, r, response)
	check.Panic(err, log, "Failed to send the response")
}

func main() {
	s, err := server.New(":443")
	if err != nil {
		panic(err)
	}

	s.Handler().Handle("/gce-xfstests", s.LoginHandler(s.FailureHandler(
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			runTests(w, r, s.Log())
		})))).Methods("POST")
	s.Handler().Handle("/internal", s.FailureHandler(
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			runTests(w, r, s.Log())
		}))).Methods("POST")
	s.Handler().Handle("/status", s.LoginHandler(s.FailureHandler(
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			status(w, r, s.Log())
		})))).Methods("POST")

	s.Start()
}
